package com.weimin.SportAppBackend.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;

/**
 * @author : huiMing
 * Date : 2025年06月23日 18:39
 * @version V1.0
 * Description: Jwt工具类,用于生成token
 */
/**
 * JWT 工具类，用于生成、解析和验证 JWT Token (使用 Auth0 Java JWT 库)
 */
@Component
public class JwtUtil {

    // 颁发者
    @Value("${jwt.issuer}")
    private String issuer;

    // JWT 秘钥
    @Value("${jwt.secret}")
    private String secret; // 确保是 private

    // JWT 过期时间 (分钟)，例如 1440 分钟 (24 小时)
    @Value("${jwt.expiration}")
    private Integer expiration; // 确保是 private

    /**
     * 生成 JWT Token
     *
     * @param subject 用户主体 (通常是手机号)
     * @param claims  自定义声明参数 (例如用户ID, 角色)
     * @return 生成的 JWT Token 字符串
     */
    public String generateToken(String subject, Map<String, Object> claims) {
        // 设置令牌的过期时间
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.MINUTE, expiration); // 根据配置的分钟数设置失效时间

        // 创建JWT builder
        JWTCreator.Builder builder = JWT.create();

        // 添加声明 (payload)
        if (claims != null) {
            // 明确地迭代并添加声明，避免方法引用可能导致的类型推断歧义
            for (Map.Entry<String, Object> entry : claims.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                // 根据值的实际类型调用对应的 withClaim 重载
                if (value instanceof String) {
                    builder.withClaim(key, (String) value);
                } else if (value instanceof Integer) {
                    builder.withClaim(key, (Integer) value);
                } else if (value instanceof Long) {
                    builder.withClaim(key, (Long) value);
                } else if (value instanceof Double) {
                    builder.withClaim(key, (Double) value);
                } else if (value instanceof Boolean) {
                    builder.withClaim(key, (Boolean) value);
                } else if (value instanceof Date) {
                    builder.withClaim(key, (Date) value);
                } else {
                    // 对于其他 Object 类型，auth0-jwt 会尝试使用 Jackson 进行序列化
                    builder.withClaim(key, (Boolean) value);
                }
            }
        }

        // 设置主题 (subject)
        builder.withSubject(subject);

        // 设置颁发者和签发时间，并签名
        return builder
                .withIssuer(issuer) // 颁发者
                .withIssuedAt(new Date()) // 签发时间
                .withExpiresAt(instance.getTime()) // 指定令牌过期时间
                .sign(Algorithm.HMAC256(secret));
    }

    /**
     * 从 Token 中提取主题 (这里是手机号)
     *
     * @param token JWT Token 字符串
     * @return 用户手机号，如果 Token 无效或无法解析则返回 null
     */
    public String extractPhone(String token) {
        try {
            DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
            return decodedJWT.getSubject();
        } catch (JWTVerificationException e) {
            // Token 验证失败（签名错误、过期等），返回 null 或抛出特定异常
            System.err.println("JWT Token 提取手机号失败: " + e.getMessage());
            return null;
        }
    }

    /**
     * 从 JWT Token 中提取主题 (subject)
     *
     * @param token JWT Token 字符串
     * @return Token 的主题 (通常是用户标识，如手机号或 OpenID)，如果 Token 无效或无法解析则返回 null
     */
    public String extractSubject(String token) {
        try {
            DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(secret)).withIssuer(issuer).build().verify(token);
            return decodedJWT.getSubject();
        } catch (JWTVerificationException e) {
            // Token 验证失败（签名错误、过期等），返回 null 或抛出特定异常
            System.err.println("JWT Token 提取主题失败: " + e.getMessage());
            return null;
        }
    }

    /**
     * 从 Token 中提取过期时间
     *
     * @param token JWT Token
     * @return 过期时间 Date 对象，如果 Token 无效或无法解析则返回 null
     */
    public Date extractExpiration(String token) {
        try {
            DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(secret)).build().verify(token);
            return decodedJWT.getExpiresAt();
        } catch (JWTVerificationException e) {
            System.err.println("JWT Token 提取过期时间失败: " + e.getMessage());
            return null;
        }
    }

    /**
     * 验证 Token 是否有效且未过期，并匹配用户手机号
     *
     * @param token     JWT Token 字符串
     * @param userPhone 从数据库获取的用户手机号
     * @return true 如果 Token 有效且未过期，并且手机号匹配，否则 false
     */
    public Boolean validateToken(String token, String userPhone) {
        try {
            // 验证 Token 的签名和过期时间
            DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(secret)).withIssuer(issuer).build().verify(token);

            // 提取 Token 中的手机号
            final String extractedPhone = decodedJWT.getSubject();

            // 检查提取出的手机号是否与用户手机号匹配，且 Token 未过期 (verify 已经包含了过期检查)
            return extractedPhone != null && extractedPhone.equals(userPhone);

        } catch (SignatureVerificationException e) {
            System.err.println("JWT Token 签名错误: " + e.getMessage());
            return false;
        } catch (AlgorithmMismatchException e) {
            System.err.println("JWT Token 算法不匹配: " + e.getMessage());
            return false;
        } catch (TokenExpiredException e) {
            System.err.println("JWT Token 已过期: " + e.getMessage());
            return false;
        } catch (Exception e) {
            System.err.println("JWT Token 验证失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 解析token并获取所有声明
     *
     * @param token JWT Token 字符串
     * @return DecodedJWT 对象，包含所有解析后的声明。如果解析失败则抛出 RuntimeException。
     */
    public DecodedJWT jwtDecode(String token) {
        try {
            return JWT.require(Algorithm.HMAC256(secret)).withIssuer(issuer).build().verify(token);
        } catch (SignatureVerificationException e) {
            throw new RuntimeException("token签名错误!", e);
        } catch (AlgorithmMismatchException e) {
            throw new RuntimeException("token算法不匹配!", e);
        } catch (TokenExpiredException e) {
            throw new RuntimeException("token过期!", e);
        } catch (Exception e) {
            throw new RuntimeException("token解析失败!", e);
        }
    }
}